package com.own.business.pay.core.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.own.business.pay.common.constant.ConstantPay;
import com.own.business.pay.common.em.PayEnum;
import com.own.business.pay.core.method.TradeExpireHandlerService;
import com.own.business.pay.core.model.PayOrderModel;
import com.own.business.pay.core.model.RefundOrderModel;
import com.own.business.pay.core.service.BillHistoryService;
import com.own.business.pay.core.util.UnitePayUtil;
import com.own.business.pay.entity.bo.BillHistoryBo;
import com.own.business.pay.entity.map.BillHistoryMapVo;
import com.own.business.pay.entity.po.BillHistory;
import com.own.business.pay.entity.query.BillHistoryQuery;
import com.own.business.pay.entity.vo.BillHistoryVo;
import com.own.business.pay.mapper.BillHistoryMapper;
import com.own.component.common.base.service.impl.AbstractBaseService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;

/**
 * BillHistoryServiceImpl
 *
 * @author chenxueli
 * @date 2023-08-22 08:53:48
 */
@Slf4j
@Service
public class BillHistoryServiceImpl extends AbstractBaseService<
        BillHistory,
        BillHistoryBo,
        BillHistoryVo,
        BillHistoryMapVo,
        BillHistoryQuery,
        BillHistoryMapper
        > implements BillHistoryService {

    @Resource
    private BillHistoryMapper billHistoryMapper;

    @Resource
    private UnitePayUtil unitePayUtil;

    @Resource
    private TradeExpireHandlerService tradeExpireHandlerService;

    /**
     * 获取查询条件
     *
     * @param query 查询条件
     * @return 查询条件对象
     */
    @Override
    public LambdaQueryWrapper<BillHistory> lambdaQueryWrapper(BillHistoryQuery query) {
        return super.queryWrapper(query)
                .eq(StringUtils.isNotBlank(query.getYear()), "date_format(gmt_create,'%Y')", query.getYear())
                .eq(StringUtils.isNotBlank(query.getMonth()), "date_format(gmt_create,'%c')", query.getMonth())
                .lambda()
                .likeRight(StringUtils.isNotBlank(query.getBillType()), BillHistory::getOutTradeNo, query.getBillType())
                .eq(query.getUserId() != null, BillHistory::getFkUserId, query.getUserId())
                .eq(StringUtils.isNotBlank(query.getPayType()), BillHistory::getTradeType, query.getPayType())
                .eq(query.getType() != null, BillHistory::getType, query.getType())
                .eq(StringUtils.isNotBlank(query.getOutTradeNo()), BillHistory::getOutTradeNo, query.getOutTradeNo())
                .eq(StringUtils.isNotBlank(query.getOutRefundNo()), BillHistory::getOutRefundNo, query.getOutRefundNo())
                .eq(query.getStatus() != null, BillHistory::getStatus, query.getStatus())
                .like(StringUtils.isNotBlank(query.getSubject()), BillHistory::getSubject, query.getSubject())
                .like(StringUtils.isNotBlank(query.getBody()), BillHistory::getBody, query.getBody())
                .ge(query.getStartDate() != null, BillHistory::getGmtCreate, query.getStartDate())
                .le(query.getEndDate() != null, BillHistory::getGmtCreate, query.getEndDate())
                .in(query.getStatuses() != null, BillHistory::getStatus, query.getStatuses())
                .notIn(query.getNotStatuses() != null, BillHistory::getStatus, query.getNotStatuses())
                .orderByDesc(BillHistory::getGmtCreate);
    }

    /**
     * 初始化数据（支出）
     *
     * @param type       支付类型
     * @param outTradeNo 商户订单号
     * @param model      请求数据
     */
    @Override
    public void initExpenditure(PayEnum.Method type, String outTradeNo, PayOrderModel model) {
        var po = new BillHistory();
        po.setSubject(model.getSubject());
        po.setBody(model.getBody());
        po.setTotalAmount(model.getTotalAmount());
        po.setTradeType(type.getType());
        po.setType(ConstantPay.Type.PAY);
        po.setOutTradeNo(outTradeNo);
        po.setFkUserId(model.getUserId());
        po.setBusinessType(model.getBusinessType());
        po.setStatus(ConstantPay.Status.NEW_BUILT);
        // 超时时间默认为15分钟
        po.setExpireTime(LocalDateTime.now().plusMinutes(15));
        billHistoryMapper.insert(po);
        // 设置订单超时的业务
        tradeExpireHandlerService.expire(ConstantPay.Type.PAY, po);
    }

    /**
     * 初始化数据（收入）
     *
     * @param type       支付类型
     * @param outTradeNo 商户订单号
     * @param model      请求数据
     */
    @Override
    public void initIncome(PayEnum.Method type, String outTradeNo, PayOrderModel model) {
        var po = new BillHistory();
        po.setSubject(model.getSubject());
        po.setBody(model.getBody());
        po.setTotalAmount(model.getTotalAmount());
        po.setTradeType(type.getType());
        po.setType(ConstantPay.Type.INCOME);
        po.setOutTradeNo(outTradeNo);
        po.setBusinessType(model.getBusinessType());
        po.setFkUserId(model.getUserId());
        // 超时时间默认为15分钟
        po.setExpireTime(LocalDateTime.now().plusMinutes(15));
        po.setStatus(ConstantPay.Status.NEW_BUILT);
        billHistoryMapper.insert(po);
        // 设置订单超时的业务
        tradeExpireHandlerService.expire(ConstantPay.Type.INCOME, po);
    }

    /**
     * 初始化数据（退款）
     *
     * @param type       支付类型
     * @param outTradeNo 商户订单号
     * @param model      请求数据
     */
    @Override
    public void initRefund(PayEnum.Method type, String outTradeNo, RefundOrderModel model) {
        var po = new BillHistory();
        po.setSubject(model.getReason());
        po.setBody(model.getReason());
        po.setTotalAmount(model.getTotal());
        po.setRefundAmount(model.getRefund());
        po.setTradeType(type.getType());
        po.setType(ConstantPay.Type.REFUND);
        po.setOutTradeNo(outTradeNo);
        po.setOutRefundNo(model.getRefundNo());
        po.setFkUserId(model.getUserId());
        po.setBusinessType(model.getBusinessType());
        po.setStatus(ConstantPay.Status.NEW_BUILT);
        // 超时时间默认为15分钟
        po.setExpireTime(LocalDateTime.now().plusMinutes(15));
        billHistoryMapper.insert(po);
        // 设置订单超时的业务
        tradeExpireHandlerService.expire(ConstantPay.Type.REFUND, po);
    }

    /**
     * 通知（成功支付）
     *
     * @param type       支付类型
     * @param outTradeNo 商户订单号
     * @param notifyData 通知数据
     */
    @Override
    public void notifySuccess(PayEnum.Method type, String outTradeNo, String notifyData) {
        var wrapper = new LambdaUpdateWrapper<BillHistory>()
                .eq(BillHistory::getStatus, ConstantPay.Status.NEW_BUILT)
                .eq(BillHistory::getOutTradeNo, outTradeNo)
                .eq(BillHistory::getTradeType, type.getType())
                .set(BillHistory::getNotifyData, notifyData)
                .set(BillHistory::getTradeTime, LocalDateTime.now())
                .set(BillHistory::getStatus, ConstantPay.Status.COMPLETE_SUCCESS);
        billHistoryMapper.update(null, wrapper);
    }

    /**
     * 通知（退款通知成功）
     *
     * @param type        支付类型
     * @param outRefundNo 退款订单号
     * @param notifyData  通知数据
     */
    @Override
    public void notifyRefundSuccess(PayEnum.Method type, String outRefundNo, String notifyData) {
        var wrapper = new LambdaUpdateWrapper<BillHistory>()
                .eq(BillHistory::getStatus, ConstantPay.Status.NEW_BUILT)
                .eq(BillHistory::getOutRefundNo, outRefundNo)
                .eq(BillHistory::getTradeType, type.getType())
                .set(BillHistory::getNotifyData, notifyData)
                .set(BillHistory::getTradeTime, LocalDateTime.now())
                .set(BillHistory::getStatus, ConstantPay.Status.COMPLETE_SUCCESS);
        billHistoryMapper.update(null, wrapper);
    }

    /**
     * 根据订单号查询信息
     * todo 需要处理多个订单号的情况
     *
     * @param tradeNo 订单号
     * @return 订单信息
     */
    @Override
    public BillHistory getByTradeNo(String tradeNo) {
        var wrapper = lambdaQueryWrapper().eq(BillHistory::getOutTradeNo, tradeNo)
                .eq(BillHistory::getType, ConstantPay.Type.PAY)
                .last("limit 1");
        return getOneByWrapper(wrapper);
    }

    /**
     * 根据订单号查询信息（收入信息）
     *
     * @param tradeNo 订单号
     * @return 订单信息
     */
    @Override
    public BillHistory getByTradeNoForIncome(String tradeNo) {
        var wrapper = lambdaQueryWrapper().eq(BillHistory::getOutTradeNo, tradeNo)
                .eq(BillHistory::getType, ConstantPay.Type.INCOME)
                .last("limit 1");
        return getOneByWrapper(wrapper);
    }

    /**
     * 获取订单列表
     *
     * @param tradeNo 订单号
     * @return 订单信息
     */
    @Override
    public List<BillHistory> listByTradeNo(String tradeNo) {
        var wrapper = lambdaQueryWrapper().eq(BillHistory::getOutTradeNo, tradeNo);
        return listByWrapper(wrapper);
    }

    /**
     * 获取订单列表
     *
     * @param tradeNoList 订单号列表
     * @return 订单信息
     */
    @Override
    public List<BillHistory> listByTradeNoList(Collection<String> tradeNoList) {
        if (tradeNoList == null || tradeNoList.isEmpty()) {
            return List.of();
        }
        var wrapper = lambdaQueryWrapper().in(BillHistory::getOutTradeNo, tradeNoList);
        return listByWrapper(wrapper);
    }

    /**
     * 根据退款单号查询信息
     *
     * @param refundNo 退款单号
     * @return 订单信息
     */
    @Override
    public BillHistory getByRefundNo(String refundNo) {
        var wrapper = lambdaQueryWrapper().eq(BillHistory::getOutTradeNo, refundNo).last("limit 1");
        return getOneByWrapper(wrapper);
    }

    /**
     * 检查退款情况
     *
     * @param outTradeNo 商户订单号
     */
    @Override
    public void checkRefund(String outTradeNo) {
        // 查询之前的订单流水信息
        var historyList = listByTradeNo(outTradeNo);
        // 验证订单信息
        var payAmount = 0;
        var refundAmount = 0;
        BillHistory tradeHistory = null;
        for (var history : historyList) {
            if (history.getType() == 1) {
                refundAmount += history.getTotalAmount();
            }
            if (history.getType() == 2) {
                payAmount += history.getTotalAmount();
                if (tradeHistory != null) {
                    continue;
                }
                tradeHistory = history;
            }
            if (history.getType() == 3) {
                refundAmount += history.getTotalAmount();
            }
        }
        if (tradeHistory == null) {
            return;
        }
        if (refundAmount >= payAmount) {
            tradeHistory.setStatus(ConstantPay.Status.COMPLETE_REFUND);
        }
        if (refundAmount < payAmount) {
            tradeHistory.setStatus(ConstantPay.Status.COMPLETE_REFUND_PART);
        }
        billHistoryMapper.updateById(tradeHistory);
    }

    /**
     * 关闭订单
     *
     * @param outTradeNo 商户订单号
     */
    @Override
    public void close(String outTradeNo) {
        // 查询订单信息
        var wrapper = lambdaQueryWrapper()
                .eq(BillHistory::getOutTradeNo, outTradeNo)
                .eq(BillHistory::getType, ConstantPay.Type.PAY)
                .eq(BillHistory::getStatus, ConstantPay.Status.NEW_BUILT);
        var po = getOneByWrapper(wrapper);
        if (po == null) {
            return;
        }
        // 去执行订单关闭
        unitePayUtil.close(po.getTradeType(), outTradeNo);
        // 修改订单状态
        po.setStatus(ConstantPay.Status.COMPLETE_CLOSE);
        po.setTradeTime(LocalDateTime.now());
        billHistoryMapper.updateById(po);
    }

}
